package communication.majiang
{
	import com.netease.protobuf.Message;
	import com.netease.protobuf.UInt64;
	
	import flash.events.Event;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;
	
	import communication.CertManager;
	import communication.ServerAddress;
	
	import modules.GameDispatcher;
	import modules.GameEvent;
	import modules.lobby.LobbyEvent;
	import modules.lobby.data.LobbyDataModel;
	import modules.lobby.data.NormalTableModel;
	import modules.majiang.MajiangEvent;
	import modules.majiang.data.CountDownState;
	import modules.majiang.data.TableDataModel;
	import modules.majiang.data.TablePlayerDataModel;
	import modules.majiang.data.TablesDataManager;
	
	import protobuf.MSPKG_AUTOHIDE_HANDTILES_REQ;
	import protobuf.MSPKG_BET_REQ;
	import protobuf.MSPKG_CALL_REQ;
	import protobuf.MSPKG_CHAT_REQ;
	import protobuf.MSPKG_DISCARD_REQ;
	import protobuf.MSPKG_ENTER_TABLE_REQ;
	import protobuf.MSPKG_FOLD_REQ;
	import protobuf.MSPKG_LEAVE_TABLE_REQ;
	import protobuf.MSPKG_PASS_REQ;
	import protobuf.MSPKG_RAISE_REQ;
	import protobuf.MSPKG_SELF_INFO_RPT;
	import protobuf.MSPKG_SHOWTILES_REQ;
	import protobuf.MSPKG_SITOUT_REQ;
	import protobuf.MSPKG_SIT_DOWN_REQ;
	import protobuf.MSPKG_STANDUP_REQ;
	import protobuf.MSPKG_TOPUP_REQ;
	import protobuf.MSPKG_UPDATE_BLIND_REQ;
	import protobuf.PKG_AUTH_ACK;
	import protobuf.PKG_AUTH_CHALLENGE_RPL;
	import protobuf.PKG_AUTH_REQ;
	import protobuf.PKG_AUTH_ACK.E_RESULT_TYPE;
	
	import utils.FloatHandler;
	import utils.ResourceManager;

	/**
	 * 发送对麻将服务器的请求
	 * @author xumin.xu
	 */
	public class MajiangMsgSender
	{
		private static var _instance:MajiangMsgSender;

		public static function get instance():MajiangMsgSender
		{
			return _instance;
		}

		public static function init():void
		{
			if (_instance == null)
			{
				logger("初始化麻将消息发送者");
				_instance = new MajiangMsgSender();
			}
		}

		/**
		 * 等待进入的桌子列表(由于 该发送者没有初始化 而临时保存的数据)
		 **/
		public static var waitEnterTableIds:Array;

		public function MajiangMsgSender()
		{
			addListeners();

			var timeOut:uint = setTimeout(function():void
			{
				if (waitEnterTableIds && waitEnterTableIds.length > 0)
				{
					for each (var tableId:UInt64 in waitEnterTableIds)
					{
						enterTable(tableId);
					}
				}
				clearTimeout(timeOut);
			}, 1000);
		}

		private function get certManager():CertManager
		{
			return CertManager.getInstance();
		}

		private function get msManager():MSManager
		{
			return MSManager.instance;
		}

		private function get dispatcher():GameDispatcher
		{
			return GameDispatcher.instance;
		}

		private function addListeners():void
		{
			dispatcher.addEventListener(MSProto.MSID_SIT_DOWN_REQ.toString(), onSitDownReq);

			dispatcher.addEventListener(MSProto.MSID_STANDUP_REQ.toString(), onStandUpReq);

			dispatcher.addEventListener(MSProto.MSID_TOPUP_REQ.toString(), onTopupReq);

			dispatcher.addEventListener(MSProto.MSID_LEAVE_TABLE_REQ.toString(), onLeaveTableReq);

			dispatcher.addEventListener(MSProto.MSID_CALL_REQ.toString(), onCallReq);

			dispatcher.addEventListener(MSProto.MSID_FOLD_REQ.toString(), onFoldReq);

			dispatcher.addEventListener(MSProto.MSID_PASS_REQ.toString(), onPassReq);

			dispatcher.addEventListener(MSProto.MSID_BET_REQ.toString(), onBetReq);

			dispatcher.addEventListener(MSProto.MSID_RAISE_REQ.toString(), onRaiseReq);

			dispatcher.addEventListener(MSProto.MSID_DISCARD_REQ.toString(), onDiscardReq);

			dispatcher.addEventListener(MSProto.MSID_CHAT_REQ.toString(), onChatSend);

			dispatcher.addEventListener(MSProto.MSID_AUTOHIDE_HANDTILES_REQ.toString(), onAutoHideHandTilesSend);

			dispatcher.addEventListener(MSProto.MSID_SITOUT_REQ.toString(), onSitOutSend);

			dispatcher.addEventListener(MSProto.MSID_SHOWTILES_REQ.toString(), onshowTilesReq);

			/** 回应用户更改性别 */
			dispatcher.addEventListener(HSProto.HSID_CHANGE_SEX_ACK.toString(), function(event:Event):void
			{
				onChangeAvatar();
			});
			/** 回应用户更改头像 */
			dispatcher.addEventListener(HSProto.HSID_CHANGE_AVATAR_ACK.toString(), function(event:Event):void
			{
				onChangeAvatar();
			});

			dispatcher.addEventListener(MSProto.MSID_UPDATE_BLIND_REQ.toString(), OnTnmtUpdateBlindReq);
		}

		/**
		 * 正在验证的socket
		 **/
		public var currentAuthSocket:MajiangSocket;

		public var currentAuthTableId:UInt64;

		/**
		 * 根据服务器地址获取麻将socket
		 * @param address 服务器地址
		 */
		public function getSocketByAddress(address:ServerAddress):MajiangSocket
		{
			return msManager.addressSocketDic[address.toString()];
		}

		/**
		 * 根据桌子编号获取麻将socket
		 * @param tableId 桌子编号
		 */
		public function getSocketByTableId(tableId:UInt64):MajiangSocket
		{
			return msManager.tableIdSocketDic[tableId.toString()];
		}

		/**
		 * 向服务器发送消息
		 * @param msgId 协议号
		 * @param message 协议内容
		 */
		private function sendMessage(tableId:UInt64, msgId:uint, message:Message):void
		{
			var socket:MajiangSocket = getSocketByTableId(tableId);
			if (socket)
			{
				socket.send(msgId, message);
			}
		}

		/**
		 * 向服务器发送消息
		 * @param address 服务器地址
		 * @param msgId 协议号
		 * @param message 协议内容
		 */
		private function sendMessageByAddress(address:ServerAddress, msgId:uint, message:Message):void
		{
			var socket:MajiangSocket = getSocketByAddress(address);
			if (socket)
			{
				socket.send(msgId, message);
			}
		}

		/**
		 * 连接麻将服
		 */
		public function connectMajiangServer(tableId:UInt64):void
		{
			var socket:MajiangSocket = getSocketByTableId(tableId);
			if (socket == null || socket.hasConnected())
				return;
			if (currentAuthSocket != null)
			{
				logger("正在登陆验证另外的麻将服");
				return;
			}
			logger("连接 桌子编号为" + tableId + "的服务器");
			socket.connect();

			currentAuthTableId = tableId;
			currentAuthSocket = socket;

			dispatcher.dispatchEvent(new GameEvent(GameEvent.SHOW_WAIT_INFO, "连接服务器中..."));

		}

		/**
		 *将保存的CLIENT_SESSION_CERT发送到服务端
		 */
		public function sendSessionCert(address:ServerAddress):void
		{
			logger("向发送服务器验证身份协议");

			var authRequest:PKG_AUTH_REQ = new PKG_AUTH_REQ();
			authRequest.clientCert = certManager.getClientSessionCert();

			sendMessageByAddress(address, HSProto.ID_PKG_AUTH_REQ, authRequest);
		}

		/**
		 * 发送挑战码
		 */
		public function sendChallenge(challengeBytes:ByteArray):void
		{
			logger("发送挑战码");

			var signBytes:ByteArray = new ByteArray();
			certManager.sign(challengeBytes, signBytes, challengeBytes.length);
			var sign:PKG_AUTH_CHALLENGE_RPL = new PKG_AUTH_CHALLENGE_RPL();
			sign.signature = signBytes;

			currentAuthSocket.send(HSProto.ID_PKG_AUTH_CHALLENGE_RPL, sign);
		}

		/**
		 * 处理麻将服验证结果
		 */
		public function handleAuthResult(response:PKG_AUTH_ACK):void
		{
			var result:int = response.result;

			switch (result)
			{
				case E_RESULT_TYPE.eSUCCESS:
					logger("通过游戏服务器身份验证");
					//标记验证通过
					currentAuthSocket.isAuth = true;
					//告诉服务器自己的头像
					OnRecvSelfInfoRpt(currentAuthSocket);
					//请求桌子内数据					
					enterTable(currentAuthTableId);
					break;
				default:
					logger("大厅服务器身份验证失败");
					break;
			}
			currentAuthTableId = null;
			currentAuthSocket = null;
		}

		/**
		 * 加入桌子
		 * @param tableId 桌子编号
		 */
		public function enterTable(tableId:UInt64):void
		{
			dispatcher.dispatchEvent(new LobbyEvent(LobbyEvent.CLOSE_TOURNAMENTINFO_UI));

			//判断是否已经在该桌子中
			if (TablesDataManager.isJoined(tableId))
			{
				dispatcher.dispatchEvent(new GameEvent(GameEvent.SHOW_TAB_TABLE, {tableIdStr: tableId.toString()}));
				return;
			}

			var socket:MajiangSocket = getSocketByTableId(tableId);
			if (!socket.isAuth || socket.hasConnected() == false)
			{
				socket.isAuth = false;
				connectMajiangServer(tableId);
				return;
			}

			logger("发送加入桌子请求");
			var enterTableReq:MSPKG_ENTER_TABLE_REQ = new MSPKG_ENTER_TABLE_REQ();
			enterTableReq.tableId = tableId;
			sendMessage(tableId, MSProto.MSID_ENTER_TABLE_REQ, enterTableReq);
		}

		/**
		 * 退出桌子
		 * @param tableId 桌子编号
		 */
		private function onLeaveTableReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;

			var leaveTableReq:MSPKG_LEAVE_TABLE_REQ = new MSPKG_LEAVE_TABLE_REQ();
			leaveTableReq.tableId = tableId;
			sendMessage(tableId, MSProto.MSID_LEAVE_TABLE_REQ, leaveTableReq);

			logger("发送请求退出" + tableId.toString() + "号座子");
		}

		/**
		 * 坐下请求
		 */
		private function onSitDownReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var chairId:uint = event.data.chairId;
			var money:Number = int(event.data.money * 100) / 100;
			
			//人物经验值小于房间要求的经验不可进入
			var normalTableModel:NormalTableModel = LobbyDataModel.cashTableDic[tableId.toString()];
			if (normalTableModel.vppRequiredLevel)
			{
				if (GlobalData.myVPPLevel < normalTableModel.vppRequiredLevel)
				{
					var data:Object = {onNoFunc: null, onYesFunc: null, showNoBtn: false};
					data.infoText = ResourceManager.getInstance().getString('MahjongResource', 'vppRequired');
					dispatcher.dispatchEvent(new GameEvent(GameEvent.SHOW_INFORMATION, data));
					return;
				}
			}

			var sitDownReq:MSPKG_SIT_DOWN_REQ = new MSPKG_SIT_DOWN_REQ();
			sitDownReq.tableId = tableId;
			sitDownReq.chairId = chairId;
			sitDownReq.money = money;
			sendMessage(tableId, MSProto.MSID_SIT_DOWN_REQ, sitDownReq);

			logger("发送请求坐下" + tableId.toString() + "号座子" + chairId.toString() + "号位置");

		}

		/**
		 * 站起请求
		 */
		private function onStandUpReq(event:MajiangEvent):void
		{
			var tableId:UInt64 = event.data.tableId;

			var standUpReq:MSPKG_STANDUP_REQ = new MSPKG_STANDUP_REQ();
			standUpReq.tableId = tableId;
			sendMessage(tableId, MSProto.MSID_STANDUP_REQ, standUpReq);

			logger("发送从" + tableId.toString() + "号座子站起请求");
		}

		/**
		 * 购买赌注请求
		 */
		private function onTopupReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var money:Number = event.data.money;

			var topupReq:MSPKG_TOPUP_REQ = new MSPKG_TOPUP_REQ();
			topupReq.tableId = tableId;
			topupReq.money = money;

			sendMessage(tableId, MSProto.MSID_TOPUP_REQ, topupReq);

			logger(tableId.toString() + "号座子" + "购买赌注请求:" + money);
		}

		/**
		 * 用户请求跟牌
		 **/
		private function onCallReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;

			var callReq:MSPKG_CALL_REQ = new MSPKG_CALL_REQ();
			callReq.tableId = tableId;

			sendMessage(tableId, MSProto.MSID_CALL_REQ, callReq);

			var tableDataModel:TableDataModel = TablesDataManager.tableDataModeDic[tableId.toString()];
			//更新玩家当前是否能操作
			var seatedPlayers:Array = tableDataModel.seatedPlayers;
			var tablePlayerDataModel:TablePlayerDataModel = seatedPlayers[tableDataModel.sitChairId];
			tablePlayerDataModel.isCurrentActionPlayer = false;

			//更新倒计时数据
			var countDownStates:Array = tableDataModel.countDownStates;
			var countDownState:CountDownState = countDownStates[tableDataModel.sitChairId];
			countDownState.stopCountDown();

			logger(tableId.toString() + "号座子" + "用户请求跟牌(跟注)");
		}

		/**
		 * 用户请求弃牌
		 **/
		private function onFoldReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;

			var foldReq:MSPKG_FOLD_REQ = new MSPKG_FOLD_REQ();
			foldReq.tableId = tableId;

			sendMessage(tableId, MSProto.MSID_FOLD_REQ, foldReq);

			var tableDataModel:TableDataModel = TablesDataManager.tableDataModeDic[tableId.toString()];
			//更新玩家当前是否能操作
			var seatedPlayers:Array = tableDataModel.seatedPlayers;
			var tablePlayerDataModel:TablePlayerDataModel = seatedPlayers[tableDataModel.sitChairId];
			tablePlayerDataModel.isCurrentActionPlayer = false;

			//更新倒计时数据
			var countDownStates:Array = tableDataModel.countDownStates;
			var countDownState:CountDownState = countDownStates[tableDataModel.sitChairId];
			countDownState.stopCountDown();

			logger(tableId.toString() + "号座子" + "用户请求弃牌");
		}

		/**
		 * 用户请求过牌
		 **/
		private function onPassReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;

			var passReq:MSPKG_PASS_REQ = new MSPKG_PASS_REQ();
			passReq.tableId = tableId;

			sendMessage(tableId, MSProto.MSID_PASS_REQ, passReq);

			var tableDataModel:TableDataModel = TablesDataManager.tableDataModeDic[tableId.toString()];
			//更新玩家当前是否能操作
			var seatedPlayers:Array = tableDataModel.seatedPlayers;
			var tablePlayerDataModel:TablePlayerDataModel = seatedPlayers[tableDataModel.sitChairId];
			tablePlayerDataModel.isCurrentActionPlayer = false;

			//更新倒计时数据
			var countDownStates:Array = tableDataModel.countDownStates;
			var countDownState:CountDownState = countDownStates[tableDataModel.sitChairId];
			countDownState.stopCountDown();

			logger(tableId.toString() + "号座子" + "用户请求过牌");
		}

		/**
		 * 用户请求下注（押注）
		 **/
		private function onBetReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;

			var money:Number = FloatHandler.TwoFormat(event.data.money);

			var betReq:MSPKG_BET_REQ = new MSPKG_BET_REQ();
			betReq.tableId = tableId;
			betReq.money = money;

			logger(tableId.toString() + "号座子" + "请求下注 " + money);

			sendMessage(tableId, MSProto.MSID_BET_REQ, betReq);

			var tableDataModel:TableDataModel = TablesDataManager.tableDataModeDic[tableId.toString()];
			//更新玩家当前是否能操作
			var seatedPlayers:Array = tableDataModel.seatedPlayers;
			var tablePlayerDataModel:TablePlayerDataModel = seatedPlayers[tableDataModel.sitChairId];
			tablePlayerDataModel.isCurrentActionPlayer = false;

			//更新倒计时数据
			var countDownStates:Array = tableDataModel.countDownStates;
			var countDownState:CountDownState = countDownStates[tableDataModel.sitChairId];
			countDownState.stopCountDown();

		}

		/**
		 * 用户请求加注
		 **/
		private function onRaiseReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var money:Number = FloatHandler.TwoFormat(event.data.money);

			var raiseReq:MSPKG_RAISE_REQ = new MSPKG_RAISE_REQ();
			raiseReq.tableId = tableId;
			raiseReq.money = money;

			sendMessage(tableId, MSProto.MSID_RAISE_REQ, raiseReq);

			var tableDataModel:TableDataModel = TablesDataManager.tableDataModeDic[tableId.toString()];
			//更新玩家当前是否能操作
			var seatedPlayers:Array = tableDataModel.seatedPlayers;
			var tablePlayerDataModel:TablePlayerDataModel = seatedPlayers[tableDataModel.sitChairId];
			tablePlayerDataModel.isCurrentActionPlayer = false;

			//更新倒计时数据
			var countDownStates:Array = tableDataModel.countDownStates;
			var countDownState:CountDownState = countDownStates[tableDataModel.sitChairId];
			countDownState.stopCountDown();

			logger(tableId.toString() + "号座子" + "用户请求加注 " + money);
		}

		/**
		 * 用户请求出牌
		 **/
		private function onDiscardReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var tile:uint = event.data.tile;

			var discardReq:MSPKG_DISCARD_REQ = new MSPKG_DISCARD_REQ();
			discardReq.tableId = tableId;
			discardReq.tile = tile;
			sendMessage(tableId, MSProto.MSID_DISCARD_REQ, discardReq);

			logger(tableId.toString() + "号座子" + "用户请求出牌,牌号为" + tile);

			var tableDataModel:TableDataModel = TablesDataManager.tableDataModeDic[tableId.toString()];

			//禁止再次出牌
			tableDataModel.disableDiscard = false;

		}

		/**
		 * 发送聊天信息
		 */
		private function onChatSend(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var info:String = event.data.info;

			var chatReq:MSPKG_CHAT_REQ = new MSPKG_CHAT_REQ();
			chatReq.tableId = tableId;
			chatReq.info = certManager.convertStringToByteArray(info);

			sendMessage(tableId, MSProto.MSID_CHAT_REQ, chatReq);

			logger(tableId.toString() + "号座子" + "发送聊天信息:" + info);
		}

		/**
		 * 请求更改自动隐藏手牌状态
		 */
		private function onAutoHideHandTilesSend(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var isHideTile:Boolean = event.data.isHideTile;

			var autoHideHandTilesReq:MSPKG_AUTOHIDE_HANDTILES_REQ = new MSPKG_AUTOHIDE_HANDTILES_REQ();
			autoHideHandTilesReq.tableId = tableId;
			autoHideHandTilesReq.isAutoHide = isHideTile;

			sendMessage(tableId, MSProto.MSID_AUTOHIDE_HANDTILES_REQ, autoHideHandTilesReq);

			logger(tableId.toString() + "号座子" + "请求更改自动隐藏手牌状态:" + isHideTile);

		}

		/**
		 * 请求修改 挂机模式（离开 下盘不参与游戏）
		 */
		private function onSitOutSend(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var isSitOut:Boolean = event.data.isSitOut;

			var sitOutReq:MSPKG_SITOUT_REQ = new MSPKG_SITOUT_REQ();
			sitOutReq.tableId = tableId;
			sitOutReq.isSitout = isSitOut;

			sendMessage(tableId, MSProto.MSID_SITOUT_REQ, sitOutReq);

			logger(tableId.toString() + "号座子" + "请求修改 挂机模式（离开 下盘不参与游戏）:" + isSitOut);
		}

		/**
		 * 请求显示手牌
		 */
		private function onshowTilesReq(event:MajiangMsgEvent):void
		{
			var tableId:UInt64 = event.data.tableId;
			var showTilesReq:MSPKG_SHOWTILES_REQ = new MSPKG_SHOWTILES_REQ();
			showTilesReq.tableId = tableId;
			showTilesReq.show = true;

			sendMessage(tableId, MSProto.MSID_SHOWTILES_REQ, showTilesReq);

			logger(tableId.toString() + "号座子" + "请求显示手牌");
		}

		/**
		 * 告诉服务器自己的头像
		 */
		private function OnRecvSelfInfoRpt(socket:MajiangSocket):void
		{
			var selfInfoRpt:MSPKG_SELF_INFO_RPT = new MSPKG_SELF_INFO_RPT();
			selfInfoRpt.avatarId = GlobalData.avatarId;
			selfInfoRpt.sex = GlobalData.sex;

			socket.send(MSProto.MSID_SELF_INFO_RPT, selfInfoRpt);
		}

		/**
		 * 通知已经连接上的麻将服 自己更改了头像
		 */
		private function onChangeAvatar():void
		{
			var addressSocketDic:Dictionary = msManager.addressSocketDic;
			if (addressSocketDic)
			{
				for each (var socket:MajiangSocket in addressSocketDic)
				{
					if (socket && socket.isAuth)
					{
						OnRecvSelfInfoRpt(socket);
					}
				}
			}
		}

		/**
		 * 请求锦标赛盲注信息
		 */
		private function OnTnmtUpdateBlindReq(event:MajiangMsgEvent):void
		{
			var tournamentId:UInt64 = event.data.tournamentId;
			var tableId:UInt64 = event.data.tableId;

			var updateBlind_req:MSPKG_UPDATE_BLIND_REQ = new MSPKG_UPDATE_BLIND_REQ();
			updateBlind_req.tnmtId = tournamentId;

			sendMessage(tableId, MSProto.MSID_UPDATE_BLIND_REQ, updateBlind_req);
		}
	}
}
